﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;


public class PlayerController : MonoBehaviour
{

    private GameObject player;

    public PlayerControllerState state;
    protected Vector3 movement;
    protected Vector2 speed;
    protected float jumpSpeed;
    protected int vDirection;
    protected int hDirection;
    protected Vector2 externalForce;
    //固定的参数
    protected const float obstacleHeightTolerance = 0.05f;     //可以爬上的障碍物高度

    protected BoxCollider2D playerCollider;
    protected Rigidbody2D playerRigidbody;
    protected Rect rayBoundsRectangle;
    protected List<RaycastHit2D> contactList;

    protected LayerMask playerMask;
    protected LayerMask platformMask;
    protected LayerMask enemyMask;

    public GameObject standingOn;   //用户所站立的对象

    [Space(10)]
    [Header("Raycasting")]
    public int NumberOfHorizontalRays = 4;
    public int NumberOfVerticalRays = 4;
    public float rayLengthOffset = 0.05f;
    public float largeValue = 50000f;

    public Vector2 Speed
    {
        get
        {
            return speed;
        }
    }

    //玩家碰撞体相对于玩家Sprite的偏移量
    public Vector3 ColliderOffset
    {
        get
        {
            //Sprite可能会放缩
            Vector3 colliderCenter = Vector3.Scale(transform.localScale, playerCollider.offset);
            return colliderCenter;
        }
    }

    public Vector3 ColliderPosition
    {
        get
        {
            Vector3 colliderPosition = transform.position + ColliderOffset;
            return colliderPosition;
        }
    }

    public Vector3 ColliderSize
    {
        get
        {
            Vector3 colliderSize = Vector3.Scale(transform.localScale, playerCollider.size);
            return colliderSize;
        }
    }

    public Vector3 BottomPosition
    {
        get
        {
            //碰撞体中线底端位置
            Vector3 colliderBottom = new Vector3(ColliderPosition.x, ColliderPosition.y - (ColliderSize.y / 2), ColliderPosition.z);
            return colliderBottom;
        }

    }

    void init()
    {
        player = gameObject;
        hDirection = 1;
        contactList = new List<RaycastHit2D>();
        playerCollider = gameObject.GetComponent<BoxCollider2D>();
        playerRigidbody = gameObject.GetComponent<Rigidbody2D>();
        state = new PlayerControllerState();
        state.init();
        initPlatformMask();
    }

    void Awake()
    {
        init();
    }

    // Update is called once per fixed frame
    void FixedUpdate()
    {
        if (speed.y <= 0 && state.IsFalling)
        {
            if (speed.y > -20)
                speed.y -= 100 * Time.fixedDeltaTime;
            else
                speed.y = -20;
        }


        if (speed.y > 0)
        {
            speed.y -= 100 * Time.fixedDeltaTime - externalForce.y * Time.fixedDeltaTime;
        }

        if(state.StayInAir)
        {
            speed.y = 0;
        }

        if (speed.x != 0)
        {
            movement.x = speed.x * Time.fixedDeltaTime;
            if (movement.x > 0)
                hDirection = 1;
            else
                hDirection = -1;
        }

        if (speed.y != 0)
        {
            movement.y = speed.y * Time.fixedDeltaTime;
            if (movement.y > 0)
                vDirection = 1;
            else
                vDirection = -1;
        }

        state.Reset();

        //更新检测用Rect
        setRayBoundsRectangle();

        //保存上一帧状态
        state.WasTouchingTheCeilingLastFrame = state.IsCollidingAbove;
        state.WasGroundedLastFrame = state.IsGrounded;

        //检测玩家是否撞壁
        castRaysBelow();
        castRaysAbove();
        castRaysHorizontal();

        //更新玩家位置
        player.transform.Translate(movement);

        //重置参数
        movement.Set(0, 0, 0);
        externalForce.Set(0, 0);
    }

    public void setVelocity(Vector2 speed)
    {
        this.speed = speed;
    }

    public void setHorizontalVelocity(float speed)
    {
        this.speed.x = speed;
    }

    public void setVerticalVelocity(float speed)
    {
        this.speed.y = speed;
    }

    public void addVerticalExternalForce(float force)
    {
        externalForce.y = force;
    }

    //检查水平方向是否碰到墙壁
    public void castRaysHorizontal()
    {
        if (movement.x == 0)
            return;

        RaycastHit2D[] hitStorage = new RaycastHit2D[NumberOfHorizontalRays];

        //计算射线长度和范围
        float rayLength = Mathf.Abs(movement.x) + rayBoundsRectangle.width / 2 + rayLengthOffset;
        Vector2 rayTop = new Vector2(rayBoundsRectangle.center.x, rayBoundsRectangle.max.y);
        Vector2 rayBottom = new Vector2(rayBoundsRectangle.center.x, rayBoundsRectangle.min.y);

        //射线检测
        for (int i = 0; i < NumberOfHorizontalRays; i++)
        {
            Vector2 rayOrigin = Vector2.Lerp(rayTop, rayBottom, (float)i / (float)(NumberOfHorizontalRays - 1));
            hitStorage[i] = DebugTools.debugRayCast(rayOrigin, Vector2.right * hDirection, rayLength, platformMask | enemyMask, true, Color.yellow, true);
            //存疑
            //且如果是斜坡,应该会穿墙
            if (hitStorage[i])
            {
                //计算撞上的障碍物的角度
                float hitAngle = Mathf.Abs(Vector2.Angle(hitStorage[i].normal, Vector2.up));
                state.SlopeAngle = hitAngle;

                if (hitAngle > PersistParameter.maximumSlopeAngle)
                {
                    if (hDirection < 0)
                        state.IsCollidingLeft = true;
                    else
                        state.IsCollidingRight = true;

                    state.SlopeAngleOK = false;

                    if (hDirection < 0)
                    {
                        movement.x = -(Mathf.Abs(hitStorage[i].point.x - rayTop.x) - rayBoundsRectangle.width / 2 - rayLengthOffset);
                    }
                    else
                    {
                        movement.x = Mathf.Abs(hitStorage[i].point.x - rayTop.x) - rayBoundsRectangle.width / 2 - rayLengthOffset;
                    }

                    contactList.Add(hitStorage[i]);
                    speed.x = 0;
                    break;
                }
            }
        }
    }

    //检查角色上方是否碰到墙壁
    public void castRaysAbove()
    {
        if (movement.y <= 0)
            return;

        RaycastHit2D[] hitStorage = new RaycastHit2D[NumberOfVerticalRays];

        //用于选择离玩家最近的地形
        bool hitConnected = false;
        float smallestDistance = largeValue;
        //计算射线长度和范围
        float rayLength = movement.y + rayBoundsRectangle.height / 2 + rayLengthOffset;
        Vector2 rayLeft = new Vector2(rayBoundsRectangle.min.x, rayBoundsRectangle.center.y);
        Vector2 rayRight = new Vector2(rayBoundsRectangle.max.x, rayBoundsRectangle.center.y);

        for (int i = 0; i < NumberOfVerticalRays; i++)
        {
            Vector2 rayOrigin = Vector2.Lerp(rayLeft, rayRight, (float)i / (float)(NumberOfVerticalRays - 1));

            hitStorage[i] = DebugTools.debugRayCast(rayOrigin, Vector2.up, rayLength, platformMask | enemyMask, true, Color.red, true);

            //算出最短距离
            if (hitStorage[i])
            {
                hitConnected = true;
                if (hitStorage[i].distance < smallestDistance)
                {
                    smallestDistance = hitStorage[i].distance;
                }
            }
        }

        if (hitConnected)
        {
            speed.y = 0;
            movement.y = smallestDistance - rayBoundsRectangle.height / 2 - rayLengthOffset;

            if ((state.IsGrounded) && (movement.y < 0))
            {
                movement.y = 0;
            }

            state.IsCollidingAbove = true;

            if (!state.WasTouchingTheCeilingLastFrame)
            {
                movement.x = 0;
                speed.x = 0;
            }
        }
    }

    //检查角色是否站在平台上
    public void castRaysBelow()
    {
        //有可能是可移动的平台
        if (movement.y > 0)
            return;

        RaycastHit2D[] hitStorage = new RaycastHit2D[NumberOfVerticalRays];

        bool hitConnected = false;
        float smallestDistance = largeValue;
        int smallestDistanceIndex = 0;
        float rayLength = Mathf.Abs(movement.y) + rayBoundsRectangle.height / 2 + rayLengthOffset;
        Vector2 rayLeft = new Vector2(rayBoundsRectangle.min.x, rayBoundsRectangle.center.y);
        Vector2 rayRight = new Vector2(rayBoundsRectangle.max.x, rayBoundsRectangle.center.y);


        for (int i = 0; i < NumberOfVerticalRays; i++)
        {
            Vector2 rayOrigin = Vector2.Lerp(rayLeft, rayRight, (float)i / (float)(NumberOfVerticalRays - 1));
            hitStorage[i] = DebugTools.debugRayCast(rayOrigin, Vector2.down, rayLength, enemyMask | platformMask, true, Color.green, true);

            if (hitStorage[i])
            {
                hitConnected = true;
                if (hitStorage[i].distance < smallestDistance)
                {
                    smallestDistance = hitStorage[i].distance;
                    smallestDistanceIndex = i;
                }
            }
        }

        //可能站立于可移动平台上,有待修改
        if (hitConnected)
        {
            speed.y = 0;
            state.IsCollidingBelow = true;
            standingOn = hitStorage[smallestDistanceIndex].collider.gameObject;

            if (state.WasGroundedLastFrame)
            {
                state.JustGotGrounded = false;
            }
            else
            {
                state.JustGotGrounded = true;
            }

            if (state.IsFalling)
            {
                if ((smallestDistance - rayBoundsRectangle.height / 2) < 0)
                {
                    movement.y = 0;
                }
                else
                    movement.y = -(smallestDistance - rayBoundsRectangle.height / 2 - rayLengthOffset);
                state.IsFalling = false;
            }
            else
            {
                movement.y = 0;
            }
        }
    }


    public void setRayBoundsRectangle()
    {
        rayBoundsRectangle = new Rect(playerCollider.bounds.min.x,
                                      playerCollider.bounds.min.y,
                                      playerCollider.bounds.size.x,
                                      playerCollider.bounds.size.y);

        Debug.DrawLine(new Vector2(rayBoundsRectangle.center.x, rayBoundsRectangle.yMin), new Vector2(rayBoundsRectangle.center.x, rayBoundsRectangle.yMax));
        Debug.DrawLine(new Vector2(rayBoundsRectangle.xMin, rayBoundsRectangle.center.y), new Vector2(rayBoundsRectangle.xMax, rayBoundsRectangle.center.y));
    }

    public void initPlatformMask()
    {
        platformMask = LayerMask.GetMask("platform");
    }


    public void addExternalForce(Vector2 externalForce)
    {
        this.externalForce = externalForce;
    }
}
